﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net;
using System.IO;
using OAuthLib;
using WTwitter.Model;
using WTwitter.Model.Twitter;
using WTwitter.Model.UserData;
using System.Diagnostics;
using System.Runtime.Serialization.Json;

namespace WTwitter.Model.Utility {
	public class AuthorizationFailedException : Exception {
		public AuthorizationFailedException(string message)
			: base(message) {
		}
	}

	/// <summary>
	/// URLに渡すパラメータ
	/// 最終的にname=valueの形に連結されてURLに付加される
	/// </summary>
	struct RequestParameter {
		public RequestParameter(string name, string value) {
			Name = name;
			Value = value;
		}

		/// <summary>
		/// パラメータの名前
		/// </summary>
		public string Name;
		/// <summary>
		/// パラメータの値
		/// ValueにはURLエンコードする前の文字列をいれる
		/// </summary>
		public string Value;
	}

	class WebUtility {
		/// <summary>
		/// URLへリクエストを送って、レスポンスを取得する。認証方法は引数のinfoで判断する。
		/// </summary>
		/// <param name="url"></param>
		/// <param name="method">"GET" or "POST"を文字列で指定する</param>
		/// <param name="parameters"></param>
		/// <param name="info"></param>
		/// <returns>Response。取得したResponseは呼び出し側でCloseすること</returns>
		public static WebResponse GetResponse(string url, string method, RequestParameter[] parameters, AuthInfo info) {
			if (info == null) {
				return GetResponseByBasicAuth(url, method, parameters, null);
			} else if (info.IsUseOAuth) {
				return GetResponseByOAuth(url, method, parameters, info);
			} else {
				return GetResponseByBasicAuth(url, method, parameters, info);
			}
		}

		/// <summary>
		/// 認証を必要としないリクエスト
		/// </summary>
		/// <param name="url"></param>
		/// <param name="method">"GET" or "POST"を文字列で指定する</param>
		/// <param name="parameters"></param>
		/// <returns>Response。取得したResponseは呼び出し側でCloseすること</returns>
		public static WebResponse GetResponseWithoutAuth(string url, string method, RequestParameter[] parameters) {
			return GetResponseByBasicAuth(url, method, parameters, null);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="url"></param>
		/// <param name="method">"GET" or "POST"を文字列で指定する</param>
		/// <param name="parameters"></param>
		/// <param name="info"></param>
		/// <returns>Response。取得したResponseは呼び出し側でCloseすること</returns>
		public static WebResponse GetResponseByOAuth(string url, string method, RequestParameter[] parameters, AuthInfo info) {
			Debug.Assert(method == "GET" || method == "POST");
			Debug.Assert(info != null);

			//パラメータ配列をOAuthLib用に変換
			var OAuthParams = (
				from item in parameters
					select new Parameter(item.Name, item.Value)
				).ToArray<Parameter>();

			var consumer = new Consumer(OAuthData.ConsumerKey, OAuthData.ConsumerSecret);
			var response = consumer.AccessProtectedResource(
				info.AccessToken,
				url,
				method,
				OAuthData.AuthorizationRealm,
				OAuthParams
				);
			return response;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="url"></param>
		/// <param name="method">"GET" or "POST"</param>
		/// <param name="parameters"></param>
		/// <param name="info"></param>
		/// <returns>Response。取得したResponseは呼び出し側でCloseすること</returns>
		public static WebResponse GetResponseByBasicAuth(string url, string method, RequestParameter[] parameters, AuthInfo info) {
			Debug.Assert(method == "GET" || method == "POST");
			string requestURL;
			StringBuilder sb;
			string paramStr = "";

			if (parameters.Length > 0) {
				//パラメータ文字列の生成
				sb = new StringBuilder();
				foreach (var p in parameters) {
					sb.AppendFormat("{0}={1}&", p.Name, p.Value);
				}
				paramStr = sb.ToString();
			}

			if (method == "GET" && parameters.Length > 0) {
				requestURL = string.Format("{0}?{1}", url, paramStr);
			} else {
				requestURL = url;
			}

			//リクエストの作成
			var request = HttpWebRequest.Create(requestURL) as HttpWebRequest;
			if (info != null) {
				request.Credentials = new NetworkCredential(info.UserName, info.Password);
			}
			request.Method = method;
			request.ServicePoint.Expect100Continue = false;
			request.ContentType = "application/x-www-form-urlencoded";

			if (method == "POST") {
				//パラメータをサーバーへ送る
				using (var stream = request.GetRequestStream())
				using (var writer = new StreamWriter(stream, Encoding.ASCII)) {
					writer.Write(paramStr);
				}
			}
			return request.GetResponse();
		}
	}

	static class WebUtility<T> where T : class {
		/// <summary>
		/// 
		/// </summary>
		/// <param name="url">リクエストするURL</param>
		/// <param name="method">"GET" or "POST"</param>
		/// <param name="parameters">リクエストのパラメータ</param>
		/// <param name="info">認証情報</param>
		/// <returns>リクエストした結果(Json形式)をT型にDeserializeしたオブジェクト</returns>
		public static T GetDeserializedJsonData(string url, string method, RequestParameter[] parameters, AuthInfo info) {
			var serializer = new DataContractJsonSerializer(typeof(T));
			T result;
			using (var response = WebUtility.GetResponse(url, method, parameters, info))
			using (var stream = response.GetResponseStream()) {
				result = serializer.ReadObject(stream) as T;
			}
			return result;
		}
	}
}
